#include "config.h"

#include "lib/libebc/ebc.h"

static char	*server = "127.0.0.1";
static ecm_uint16	port;
ECMBOOL	server_mode;

static ecm_sockfd_t	sockfd;

static ecm_int32	datalen = 4096;
static ecm_int32	count = 1;

static void
usage(void)
{
	printf(
"Usage: socktest <options>\n"
"    -s <server>: server ip\n"
"    -l : server mode\n"
"    -p <port>: port number\n"
"    -d <data len>\n"
"    -c <count>\n"
		);
}

static void
parse_args(int argc, char *argv[])
{
	int	c;

	while ((c = ecm_getopt(argc, argv, "s:lhp:d:c:")) != -1) {
		switch (c) {
		case 's':
			server = ecm_strdup(ecm_optarg);
			break;
		case 'p':
			if (ecm_sscanf(ecm_optarg, "%hu", &port) != 1) {
				usage();
				exit(1);
			}
			break;
		case 'l':
			server_mode = ECM_TRUE;
			break;
		case 'd':
			if (ecm_sscanf(ecm_optarg, "%u", &datalen) != 1) {
				usage();
				exit(1);
			}
			break;
		case 'c':
			if (ecm_sscanf(ecm_optarg, "%hu", &count) != 1) {
				usage();
				exit(1);
			}
			break;
		case 'h':
			usage();
			exit(0);
		default:
			usage();
			exit(1);
		}
	}
}

static void
print_perf(ecm_perf_t perf)
{
	ecm_show_perf(perf, ecm_stdout, "Bandwidth: %b, User:%u, Sys:%s\n");
}

static void
do_process(ecm_sockfd_t fd)
{
	ecm_uint32	i;
	char	*buf;
	ecm_size_t	datalen, nrecvs, count, buflen;
	ecm_perf_t	perf;
	ebc_err_t	err;

	buflen = sizeof(datalen);
	ecm_sock_recv(fd, (char *)&datalen, &buflen, 0, NULL);
	buflen = sizeof(count);
	ecm_sock_recv(fd, (char *)&count, &buflen, 0, NULL);

	buf = ecm_malloc(datalen);
	perf = ecm_create_perf(1);
	ecm_start_perf(perf);
	for (i = 0; i < count; i++) {
		nrecvs = datalen;
		if ((err = ecm_sock_recv(fd, buf, &nrecvs, ECM_SOCK_TRANSFLAG_WAITALL, NULL)) != EBC_OK) {
			printf("cannot receive: %d\n", err);
			break;
		}
		if (nrecvs != datalen) {
			ecm_fprintf(ecm_stderr, "nrecvs(%u) != datalen(%u)\n", nrecvs, datalen);
			break;
		}
	}
	ecm_add_perf_data(perf, 0, datalen * count);
	ecm_stop_perf(perf);
	ecm_free(buf);
	ecm_sock_close(fd);

	print_perf(perf);
	ecm_destroy_perf(perf);
}

static int
do_server(void)
{
	ecm_sockaddr_t	addr;
	ecm_sockfd_t	clisockfd;
	int	one = 1;

	if (!ecm_sock_get_sockaddr_ipv4(NULL, port, addr))
		return 3;

	ecm_sock_setsockopt(sockfd, ECM_SOCK_OPT_REUSEADDR, (char *)&one, sizeof(one));

	if (ecm_sock_bind(sockfd, addr) != EBC_OK) {
		fprintf(stderr, "cannot bind\n");
		return 3;
	}
	if (ecm_sock_listen(sockfd, 5) != EBC_OK) {
		fprintf(stderr, "cannot linstne\n");
		return 4;
	}

	while (ECM_TRUE) {
		if (ecm_sock_accept(sockfd, &clisockfd, NULL) != EBC_OK) {
			fprintf(stderr, "cannot accept\n");
			return 5;
		}
		do_process(clisockfd);
	}
	return 0;
}

static int
do_client(void)
{
	ecm_sockaddr_t	addr;
	char	*buf;
	ecm_size_t	buflen;
	ecm_perf_t	perf;
	ecm_int32	i;

	if (!ecm_sock_get_sockaddr_ipv4(server, port, addr)) {
		fprintf(stderr, "%s: invalid address\n", server);
		return 2;
	}

	if (ecm_sock_connect(sockfd, addr, 5000) != EBC_OK) {
		fprintf(stderr, "Cannot connect\n");
		return 3;
	}

	buflen = sizeof(datalen);
	ecm_sock_send(sockfd, (char *)&datalen, &buflen, 0, ECM_TRUE);
	buflen = sizeof(count);
	ecm_sock_send(sockfd, (char *)&count, &buflen, 0, ECM_TRUE);

	perf = ecm_create_perf(0);
	ecm_start_perf(perf);
	buf = ecm_malloc(datalen);
	for (i = 0; i < count; i++) {
		ebc_err_t	err;

		buflen = datalen;
		if ((err = ecm_sock_send(sockfd, buf, &buflen, 0, ECM_TRUE)) != EBC_OK || buflen != datalen) {
			fprintf(stderr, "cannot send: %d\n", err);
			return 4;
		}

	}
	ecm_add_perf_data(perf, 0, datalen * count);
	ecm_stop_perf(perf);
	ecm_free(buf);
	ecm_sock_close(sockfd);

	print_perf(perf);
	ecm_destroy_perf(perf);

	return 0;
}

int
main(int argc, char *argv[])
{
	ebc_initialize();

	parse_args(argc, argv);

	if ((sockfd = ecm_sock_socket(ECM_SOCK_TYPE_TCP)) == NULL) {
		fprintf(stderr, "cannot create socket\n");
		exit(2);
	}
		
	if (server_mode)
		return do_server();
	else
		return do_client();
}
